﻿/*
** Xin YUAN, 2021, BSD (2)
*/

////////////////////////////////////////////////////////////////////////////////
#ifndef __CTL_MDA_H__
#define __CTL_MDA_H__
////////////////////////////////////////////////////////////////////////////////

#ifndef __CTL_DEF_H__
	#error mda.h requires def.h to be included first.
#endif

/*
Multi-dimensional Array
*/

////////////////////////////////////////////////////////////////////////////////
namespace CTL {
////////////////////////////////////////////////////////////////////////////////

#pragma pack(push, 1)

// DimInfoT<T, t_uDimNum>
/*! \brief A class for dimensional information.

A class for dimensional information.
\tparam T Information type, such as intptr_t, uintptr_t.
\tparam t_uDimNum The number of dimensions. It can not be 0.
\note
\verbatim
Dimension order:
0 --- columns, X
1 --- rows, Y
2 --- slices, Z
3 --- time, T
4 --- ...
...
\endverbatim
*/
template <typename T, uintptr_t t_uDimNum>
class DimInfoT
{
public:
	static constexpr const uintptr_t c_uDimNum = t_uDimNum;

public:
	/*! \brief Constructor.

	Constructor.
	*/
	DimInfoT() noexcept
	{
		assert( t_uDimNum > 0 );
	}
	/*! \brief Copy Constructor.

	Copy Constructor.
	*/
	DimInfoT(const DimInfoT& src) noexcept
	{
		MemoryTraits::Copy(src.m_info, t_uDimNum * sizeof(T), m_info);
	}
	DimInfoT(DimInfoT&& src) noexcept
	{
		MemoryTraits::Copy(src.m_info, t_uDimNum * sizeof(T), m_info);
		MemoryTraits::Zero(src.m_info, t_uDimNum * sizeof(T));
	}
	/*! \brief Constructor.

	Constructor.
	\param p [in] The source array.
	*/
	DimInfoT(const T* p) noexcept
	{
		MemoryTraits::Copy(p, t_uDimNum * sizeof(T), m_info);
	}
	~DimInfoT() noexcept
	{
	}

	/*! \brief Assignment operator.

	Assignment operator.
	*/
	DimInfoT& operator=(const DimInfoT& src) noexcept
	{
		if ( this != &src )
			MemoryTraits::Copy(src.m_info, t_uDimNum * sizeof(T), m_info);
		return *this;
	}
	DimInfoT& operator=(DimInfoT&& src) noexcept
	{
		if ( this != &src ) {
			MemoryTraits::Copy(src.m_info, t_uDimNum * sizeof(T), m_info);
			MemoryTraits::Zero(src.m_info, t_uDimNum * sizeof(T));
		}
		return *this;
	}
	/*! \brief Assignment operator.

	Assignment operator.
	\param p [in] The source array.
	*/
	DimInfoT& operator=(const T* p) noexcept
	{
		assert( m_info != p );
		MemoryTraits::Copy(p, t_uDimNum * sizeof(T), m_info);
		return *this;
	}

	const T& operator[](uintptr_t uIndex) const noexcept
	{
		assert( uIndex < t_uDimNum );
		return m_info[uIndex];
	}
	T& operator[](uintptr_t uIndex) noexcept
	{
		assert( uIndex < t_uDimNum );
		return m_info[uIndex];
	}

	/*! \brief Get the dimensional information.

	Get the dimensional information.
	\return A pointer to dimensional information.
	*/
	const T* GetPointer() const noexcept
	{
		return m_info;
	}
	T* GetPointer() noexcept
	{
		return m_info;
	}

	/*! \brief Clear the dimensional information.

	Clear the dimensional information.
	*/
	void Clear() noexcept
	{
		MemoryTraits::Zero(m_info, t_uDimNum * sizeof(T));
	}

private:
	T m_info[t_uDimNum];
};

template <typename T, uintptr_t t_uDimNum>
inline
DimInfoT<T, t_uDimNum>& PointerToDimInfo(T* p) noexcept
{
	return *((DimInfoT<T, t_uDimNum>*)(void*)p);
}

// DimInfoHelper
/*! \brief A helper class for dimensional information.

A helper class for dimensional information.
*/
class DimInfoHelper
{
public:
	/*! \brief Calculate the total size.

	Calculate the total size.
	\tparam t_uDimNum The number of dimensions. It can not be 0.
	\param info [in] Specify the dimensional information (Column is the first element).
	\param result [out] Receive the total size.
	\return true for succeeded, false for overflow.
	*/
	template <uintptr_t t_uDimNum>
	static bool CalcTotalSize(const DimInfoT<uintptr_t, t_uDimNum>& info, uintptr_t& result) noexcept
	{
		result = 1;
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			if ( !ArithmeticTraits<uintptr_t>::Multiply(result, info[i], result) )
				return false;
		}
		return true;
	}
	/*! \brief Calculate the absolute index.

	Calculate the element's absolute index.
	\tparam t_uDimNum The number of dimensions. It can not be 0.
	\param info [in] Specify the dimensional information (Column is the first element).
	\param indices [in] Specify the dimensional indices (Column is the first element).
	\param result [out] Receive the absolute index of that element.
	\return true for succeeded, false for overflow.
	*/
	template <uintptr_t t_uDimNum>
	static bool CalcAbsIndex(const DimInfoT<uintptr_t, t_uDimNum>& info, const DimInfoT<uintptr_t, t_uDimNum>& indices, uintptr_t& result) noexcept
	{
		assert( indices[t_uDimNum - 1] < info[t_uDimNum - 1] );
		result = indices[t_uDimNum - 1];
		for ( uintptr_t i = t_uDimNum - 1; i > 0; i -- ) {
			assert( indices[i - 1] < info[i - 1] );
			if ( !ArithmeticTraits<uintptr_t>::Multiply(result, info[i - 1], result) )
				return false;
			if ( !ArithmeticTraits<uintptr_t>::Add(result, indices[i - 1], result) )
				return false;
		}
		return true;
	}
	template <uintptr_t t_uDimNum>
	static uintptr_t CalcAbsIndex(const DimInfoT<uintptr_t, t_uDimNum>& info, const DimInfoT<uintptr_t, t_uDimNum>& indices) noexcept
	{
		assert( indices[t_uDimNum - 1] < info[t_uDimNum - 1] );
		uintptr_t result = indices[t_uDimNum - 1];
		for ( uintptr_t i = t_uDimNum - 1; i > 0; i -- ) {
			assert( indices[i - 1] < info[i - 1] );
			result *= info[i - 1];
			result += indices[i - 1];
		}
		return result;
	}
	/*! \brief Calculate the dimensional indices.

	Calculate the dimensional indices.
	\tparam t_uDimNum The number of dimensions. It can not be 0.
	\param uElement [in] The absolute index.
	\param info [in] Specify the dimensional information (Column is the first element).
	\param indices [out] Receive the dimensional indices (Column is the first element).
	*/
	template <uintptr_t t_uDimNum>
	void CalcDimIndex(uintptr_t uElement, const DimInfoT<uintptr_t, t_uDimNum>& info, DimInfoT<uintptr_t, t_uDimNum>& indices) noexcept
	{
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			assert( info[i] != 0 );
			uintptr_t v = uElement / info[i];
			indices[i] = uElement - v * info[i];
			uElement = v;
		} //end for
	}
};

// MdaView<t_uDimNum>
/*! \brief A view class for multi-dimensional array.

A view class for multi-dimensional array.
\tparam t_uDimNum The number of dimensions.
*/
template <uintptr_t t_uDimNum>
class MdaView
{
public:
	/*! \brief Constructor.

	Constructor.
	*/
	MdaView() noexcept
	{
	}
	/*! \brief Copy constructor.

	Copy constructor.
	*/
	MdaView(const MdaView& src) noexcept : m_dimStarts(src.m_dimStarts),
											m_dimSteps(src.m_dimSteps),
											m_dimEnds(src.m_dimEnds)
	{
	}
	MdaView(MdaView&& src) noexcept : m_dimStarts(rv_forward(src.m_dimStarts)),
									m_dimSteps(rv_forward(src.m_dimSteps)),
									m_dimEnds(rv_forward(src.m_dimEnds))
	{
	}
	~MdaView() noexcept
	{
	}

	/*! \brief Assignment operator.

	Assignment operator.
	*/
	MdaView& operator=(const MdaView& src) noexcept
	{
		m_dimStarts = src.m_dimStarts;
		m_dimSteps = src.m_dimSteps;
		m_dimEnds = src.m_dimEnds;
		return *this;
	}
	MdaView& operator=(MdaView&& src) noexcept
	{
		m_dimStarts = rv_forward(src.m_dimStarts);
		m_dimSteps = rv_forward(src.m_dimSteps);
		m_dimEnds = rv_forward(src.m_dimEnds);
		return *this;
	}

	/*! \brief Get the start dimensional information.

	Get the start dimensional information.
	\return A reference to the start dimensional information.
	*/
	const DimInfoT<uintptr_t, t_uDimNum>& GetStarts() const noexcept
	{
		return m_dimStarts;
	}
	DimInfoT<uintptr_t, t_uDimNum>& GetStarts() noexcept
	{
		return m_dimStarts;
	}
	/*! \brief Get the step dimensional information.

	Get the step dimensional information.
	\return A reference to the step dimensional information.
	*/
	const DimInfoT<uintptr_t, t_uDimNum>& GetSteps() const noexcept
	{
		return m_dimSteps;
	}
	DimInfoT<uintptr_t, t_uDimNum>& GetSteps() noexcept
	{
		return m_dimSteps;
	}
	/*! \brief Get the end dimensional information.

	Get the end dimensional information.
	\return A reference to the end dimensional information.
	*/
	const DimInfoT<uintptr_t, t_uDimNum>& GetEnds() const noexcept
	{
		return m_dimEnds;
	}
	DimInfoT<uintptr_t, t_uDimNum>& GetEnds() noexcept
	{
		return m_dimEnds;
	}

	const uintptr_t* GetPointer() const noexcept
	{
		return m_dimStarts.GetPointer();
	}
	uintptr_t* GetPointer() noexcept
	{
		return m_dimStarts.GetPointer();
	}

private:
	DimInfoT<uintptr_t, t_uDimNum>  m_dimStarts;  //!< The start dimensions.
	DimInfoT<uintptr_t, t_uDimNum>  m_dimSteps;   //!< The dimensional steps.
	DimInfoT<uintptr_t, t_uDimNum>  m_dimEnds;    //!< The end dimensions (Ni+1).
};

template <uintptr_t t_uDimNum>
inline
MdaView<t_uDimNum>& PointerToMdaView(uintptr_t* p) noexcept
{
	return *((MdaView<t_uDimNum>*)(void*)p);
}

// MdaExpandModes
/*! \brief An enumerator for expanding modes of multi-dimensional array.

An enumerator for expanding modes (spread mode) of multi-dimensional array.
*/
enum MdaExpandModes
{
	MDA_EM_CONST = 0,  //!< The const value as the expanding value.
	MDA_EM_MIRROR,     //!< Mirror expanding.
	MDA_EM_BORDER,     //!< Border expanding.
	MDA_EM_PERIOD,     //!< Period expanding.
	MDA_EM_MAX         //!< The maximum value of expanding modes.
};

// MdArray<T, t_uDimNum>

template <uintptr_t t_uDimNum>
class _md_array_base
{
protected:
	struct _header
	{
		uintptr_t uAllocBytes;                      //!< The allocated bytes.
		DimInfoT<uintptr_t, t_uDimNum> dimSizes;    //!< MDA size
		DimInfoT<uintptr_t, t_uDimNum> dimExtras;
		DimInfoT<uintptr_t, t_uDimNum> dimStrides;  //!< MDA stride
	};

protected:
	_md_array_base() noexcept : m_p(NULL)
	{
	}
	_md_array_base(const _md_array_base& src) = delete;
	_md_array_base(_md_array_base&& src) noexcept : m_p(src.m_p)
	{
		src.m_p = NULL;
	}
	~_md_array_base() noexcept
	{
		dump();
	}

	_md_array_base& operator=(const _md_array_base& src) = delete;
	_md_array_base& operator=(_md_array_base&& src) noexcept
	{
		if ( this != &src ) {
			if ( m_p == src.m_p ) {
				assert( m_p == NULL );
			}
			else {
				dump();
				m_p = src.m_p;
				src.m_p = NULL;
			}
		}
		return *this;
	}

	void dump() noexcept
	{
		if ( m_p != NULL ) {
			AllocatorTraits::Free(m_p);
			m_p = NULL;
		}
	}

	void set_size(uintptr_t uBytesPerElement, const uintptr_t* pSizes, const uintptr_t* pExtras)
	{
		uintptr_t uBytes = uBytesPerElement;
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			assert( pSizes[i] > 0 );
			uBytes = ArithmeticTraits<uintptr_t>::MultiplyThrow(uBytes, pSizes[i]);  //may throw
			uBytes = ArithmeticTraits<uintptr_t>::AddThrow(uBytes, pExtras[i]);  //may throw
		}
		uintptr_t uAllocBytes = uBytes;
		uBytes = ArithmeticTraits<uintptr_t>::AddThrow(uBytes, (uintptr_t)sizeof(_header));  //may throw
		_header* ph = get_header();
		if ( m_p == NULL ) {
			m_p = AllocatorTraits::Allocate(uBytes);
			if ( m_p == NULL )
				ExceptionTraits::ThrowMemory();  //throw
			ph = get_header();
			ph->uAllocBytes = uAllocBytes;
		}
		if ( uAllocBytes > ph->uAllocBytes ) {
			void* pTemp = AllocatorTraits::Reallocate(m_p, uBytes);
			if ( pTemp == NULL )
				ExceptionTraits::ThrowMemory();  //throw
			m_p = pTemp;
			ph = get_header();
			ph->uAllocBytes = uAllocBytes;
		}
		ph->dimSizes = pSizes;
		ph->dimExtras = pExtras;
		uintptr_t* phStrides = ph->dimStrides.GetPointer();
		phStrides[0] = uBytesPerElement;
		for ( uintptr_t i = 1; i < t_uDimNum; i ++ )
			phStrides[i] = phStrides[i - 1] * pSizes[i - 1] + pExtras[i - 1];
	}

	void shrink() noexcept
	{
		_header* ph = get_header();
		if ( ph == NULL )
			return ;
		const uintptr_t* phSizes = ph->dimSizes.GetPointer();
		const uintptr_t* phExtras = ph->dimExtras.GetPointer();
		const uintptr_t* phStrides = ph->dimStrides.GetPointer();
		uintptr_t uBytes = phStrides[0];
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			uBytes *= phSizes[i];
			uBytes += phExtras[i];
		}
		assert( uBytes <= ph->uAllocBytes );
		if ( uBytes == ph->uAllocBytes )
			return ;
		void* pTemp = AllocatorTraits::Reallocate(m_p, uBytes + sizeof(_header));
		if ( pTemp == NULL )
			return ;
		m_p = pTemp;
		ph = get_header();
		ph->uAllocBytes = uBytes;
	}

	void* clone()
	{
		_header* ph = get_header();
		if ( ph == NULL )
			return NULL;
		uintptr_t uBytes = ph->uAllocBytes + sizeof(_header);
		void* p = AllocatorTraits::Allocate(uBytes);
		if ( p == NULL )
			ExceptionTraits::ThrowMemory();  //throw
		MemoryTraits::Copy(m_p, uBytes, p);
		return p;
	}

protected:
	const _header* get_header() const noexcept
	{
		return (const _header*)m_p;
	}
	_header* get_header() noexcept
	{
		return (_header*)m_p;
	}

	const void* get_data() const noexcept
	{
		return get_header() + 1;
	}
	void* get_data() noexcept
	{
		return get_header() + 1;
	}

	//calculate data index in bytes
	/*! \brief calculate the absolute index.

	calculate the element's absolute index.
	\param pIndices [in] Specify the dimensional indices (Column is the first element).
	\return The absolute index of that element.
	*/
	uintptr_t calc_index(const uintptr_t* pIndices) const noexcept
	{
		const uintptr_t* phStrides = get_header()->dimStrides.GetPointer();
		uintptr_t uIndex = 0;
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			assert( pIndices[i] < get_header()->dimSizes[i] );
			uIndex += (pIndices[i] * phStrides[i]);
		}
		return uIndex;
	}

	const void* get_at(const uintptr_t* pIndices) const noexcept
	{
		return (const uint8_t*)get_data() + calc_index(pIndices);
	}
	void* get_at(const uintptr_t* pIndices) noexcept
	{
		return (uint8_t*)get_data() + calc_index(pIndices);
	}

//iterators

	class _iterator
	{
	protected:
		//copy constructor & assignment operator: bit-copy

		bool is_end() const noexcept
		{
			return m_dimIndices[t_uDimNum - 1] == m_pView->GetEnds()[t_uDimNum - 1];
		}

		void to_next() noexcept
		{
			const uintptr_t* pvStarts = m_pView->GetStarts().GetPointer();
			const uintptr_t* pvSteps = m_pView->GetSteps().GetPointer();
			const uintptr_t* pvEnds = m_pView->GetEnds().GetPointer();
			const uintptr_t* phStrides = m_pArray->get_header()->dimStrides.GetPointer();
			const intptr_t* pSigns = m_dimSigns.GetPointer();
			uintptr_t* pIndices = m_dimIndices.GetPointer();
			uintptr_t uRest;
			uintptr_t i = 0;
			for ( ; i < t_uDimNum - 1; i ++ ) {
				uRest = pSigns[i] * (pvEnds[i] - pIndices[i]);
				if ( uRest > pvSteps[i] ) {
					m_pData = (uint8_t*)m_pData + pSigns[i] * pvSteps[i] * phStrides[i];
					pIndices[i] += (pSigns[i] * pvSteps[i]);
					return ;
				}
				m_pData = (uint8_t*)m_pData - (pIndices[i] - pvStarts[i]) * phStrides[i];
				pIndices[i] = pvStarts[i];
			}
			uRest = pSigns[i] * (pvEnds[i] - pIndices[i]);
			if ( uRest > pvSteps[i] )
				uRest = pvSteps[i];
			pIndices[i] += (pSigns[i] * uRest);
			m_pData = (uint8_t*)m_pData + pSigns[i] * uRest * phStrides[i];
		}

	protected:
		DimInfoT<intptr_t, t_uDimNum> m_dimSigns;
		//variant
		DimInfoT<uintptr_t, t_uDimNum> m_dimIndices;  //!< The current dimensional indices.
		void* m_pData;  //!< A pointer to current element.
		//basic
		const MdaView<t_uDimNum>* m_pView;  //!< A pointer to dimensional view.
		const _md_array_base* m_pArray;  //!< A pointer to collection.

	private:
		friend class _md_array_base;
	};

	void get_iterator(const MdaView<t_uDimNum>& view, _iterator& iter) const noexcept
	{
		assert( m_p != NULL );

		iter.m_pView = &view;
		iter.m_pArray = this;

		const uintptr_t* pvStarts = view.GetStarts().GetPointer();
		const uintptr_t* pvSteps = view.GetSteps().GetPointer();
		(void)pvSteps;
		const uintptr_t* pvEnds = view.GetEnds().GetPointer();
		const uintptr_t* phSizes = get_header()->dimSizes.GetPointer();
		(void)phSizes;

		DimInfoT<uintptr_t, t_uDimNum> dimActStarts(pvStarts);
		for ( uintptr_t i = 0; i < t_uDimNum; i ++ ) {
			assert( (pvStarts[i] < phSizes[i])
				|| (pvStarts[i] == phSizes[i]
					&& pvEnds[i] < pvStarts[i]) );
			assert( pvSteps[i] > 0 );
			assert( pvEnds[i] <= phSizes[i] );
			iter.m_dimSigns[i] = 1;
			if ( pvEnds[i] < pvStarts[i] ) {
				-- (dimActStarts[i]);
				iter.m_dimSigns[i] = (intptr_t)-1;
			}
		}
		iter.m_dimIndices = pvStarts;
		iter.m_pData = (uint8_t*)get_data() + calc_index(dimActStarts.GetPointer());
	}

protected:
	void* m_p;  //!< A pointer to data.
};

/*
Neighbourhood : a set of DimInfoT<intptr_t, t_uDimNum>
*/

/*! \brief Multi-dimensional array.

Multi-dimensional array.
\tparam T A POD type for elements.
\tparam t_uDimNum The number of dimensions.
*/
template <typename T, uintptr_t t_uDimNum>
class MdArray : public _md_array_base<t_uDimNum>
{
private:
	typedef _md_array_base<t_uDimNum>  baseClass;

public:
	typedef T  ValueType;
	static constexpr const uintptr_t c_uDimNum = t_uDimNum;

public:
	/*! \brief Constructor.

	Constructor.
	*/
	MdArray() noexcept
	{
	}
	MdArray(const MdArray& src) = delete;
	/*! \brief Copy Constructor.

	Copy Constructor.
	*/
	MdArray(MdArray&& src) noexcept : baseClass(rv_forward(static_cast<baseClass&>(src)))
	{
	}
	/*! \brief Destructor.

	Destructor.
	*/
	~MdArray() noexcept
	{
	}

	MdArray& operator=(const MdArray& src) = delete;
	/*! \brief Assignment operator.

	Assignment operator.
	*/
	MdArray& operator=(MdArray&& src) noexcept
	{
		baseClass::operator=(rv_forward(static_cast<baseClass&>(src)));
		return *this;
	}

	/*! \brief Remove all elements.

	Remove all elements.
	*/
	void Free() noexcept
	{
		baseClass::dump();
	}

	/*! \brief Check if the array is empty.

	Check if the array is empty.
	\return true for empty, false for non-empty.
	*/
	bool IsNull() const noexcept
	{
		return baseClass::get_header() == NULL;
	}

	/*! \brief Set the dimensional sizes.

	Set the dimensional sizes.
	\param dimSizes [in] Specify the dimensional sizes (Column is the first element).
	\param dimExtras [in] Specify the dimensional extra bytes (Column is the first element).
	\note It may throw exceptions.
	*/
	void SetSize(const DimInfoT<uintptr_t, t_uDimNum>& dimSizes, const DimInfoT<uintptr_t, t_uDimNum>& dimExtras)
	{
		baseClass::set_size(sizeof(T), dimSizes.GetPointer(), dimExtras.GetPointer());  //may throw
	}

	/*! \brief Get the sizes.

	Get the sizes.
	\return A reference to dimensional sizes.
	*/
	const DimInfoT<uintptr_t, t_uDimNum>& GetSize() const noexcept
	{
		assert( !IsNull() );
		return baseClass::get_header()->dimSizes;
	}
	/*! \brief Get the dimensional extras.

	Get the dimensional extras.
	\return A reference to the dimensional extras.
	*/
	const DimInfoT<uintptr_t, t_uDimNum>& GetExtra() const noexcept
	{
		assert( !IsNull() );
		return baseClass::get_header()->dimExtras;
	}

	/*! \brief Get an element.

	Get an element.
	\param dimIndices [in] Specify the dimensional indices (Column is the first element).
	\return A reference to element.
	*/
	const T& GetAt(const DimInfoT<uintptr_t, t_uDimNum>& dimIndices) const noexcept
	{
		assert( !IsNull() );
		return *((const T*)baseClass::get_at(dimIndices.GetPointer()));
	}
	T& GetAt(const DimInfoT<uintptr_t, t_uDimNum>& dimIndices) noexcept
	{
		assert( !IsNull() );
		return *((T*)baseClass::get_at(dimIndices.GetPointer()));
	}

	/*! \brief Free extra memory.

	Free extra memory.
	*/
	void Shrink() noexcept
	{
		baseClass::shrink();
	}

	MdArray<T, t_uDimNum> Clone()
	{
		MdArray<T, t_uDimNum> ret;
		ret.m_p = baseClass::clone();  //may throw
		return ret;
	}

//Iterators
	/*! \brief An iterator class for multi-dimensional array.

	An iterator class for multi-dimensional array.
	*/
	class Iterator : public baseClass::_iterator
	{
	private:
		typedef typename baseClass::_iterator  iteratorBaseClass;

	public:
		bool IsEnd() const noexcept
		{
			return iteratorBaseClass::is_end();
		}

		/*! \brief Get the current dimensional information.

		Get the current dimensional information.
		\return A reference to the current dimensional information.
		*/
		const DimInfoT<uintptr_t, t_uDimNum>& GetIndices() const noexcept
		{
			return iteratorBaseClass::m_dimIndices;
		}

		const T& V() const noexcept
		{
			return *((const T*)iteratorBaseClass::m_pData);
		}
		T& V() noexcept
		{
			return *((T*)iteratorBaseClass::m_pData);
		}

		void ToNext() noexcept
		{
			iteratorBaseClass::to_next();
		}

	private:
		friend class MdArray<T, t_uDimNum>;
	};

	/*! \brief Initialize the iterator.

	Initialize the iterator.
	\param view [in] The dimensional view.
	\param iter [out] Receive the iterator.
	\note The iterator is bound to the view object. Do not change the view object
	      while looping the iterator.
	*/
	void GetIterator(const MdaView<t_uDimNum>& view, Iterator& iter) const noexcept
	{
		baseClass::get_iterator(view, iter);
	}

private:
	friend class MdArrayHelper;
};

#pragma pack(pop)

// MdArrayHelper

class MdArrayHelper
{
public:
	/*! \brief Get the pointer to array.

	Get the pointer to array.
	\tparam T The element type.
	\tparam t_uDimNum The number of dimensions.
	\param sp [in] Specify a multi-dimensional array.
	\return A pointer to array.
	*/
	template <typename T, uintptr_t t_uDimNum>
	static T* GetDataPointer(const MdArray<T, t_uDimNum>& sp) noexcept
	{
		return sp.IsNull() ? NULL : (T*)sp.get_data();
	}
};

// MDA_ForEach

template <class TIterator, class TFunctor>
inline void MDA_ForEach(TIterator& iter, const TFunctor& func)
{
	for ( ; !iter.IsEnd(); iter.ToNext() )
		func(iter.V());
}
template <class TIterator1, class TIterator2, class TFunctor>
inline void MDA_ForEach(TIterator1& iter1, TIterator2& iter2, const TFunctor& func)
{
	for ( ; !iter1.IsEnd(); iter1.ToNext() ) {
		assert( !iter2.IsEnd() );
		func(iter1.V(), iter2.V());
		iter2.ToNext();
	}
}

// MDA_ForEachFunc

template <class TIterator1, class TIterator2, class TFunctor>
inline void MDA_ForEachFunc(TIterator1& iter1, TIterator2& iter2, const TFunctor& func)
{
	for ( ; !iter1.IsEnd(); iter1.ToNext() ) {
		assert( !iter2.IsEnd() );
		iter2.V() = func(iter1.V());
		iter2.ToNext();
	}
}
template <class TIterator1, class TIterator2, class TIterator, class TFunctor>
inline void MDA_ForEachFunc(TIterator1& iter1, TIterator2& iter2, TIterator& iter,
				const TFunctor& func)
{
	for ( ; !iter1.IsEnd(); iter1.ToNext() ) {
		assert( !iter2.IsEnd() );
		assert( !iter.IsEnd() );
		iter.V() = func(iter1.V(), iter2.V());
		iter2.ToNext();
		iter.ToNext();
	}
}

////////////////////////////////////////////////////////////////////////////////
}
////////////////////////////////////////////////////////////////////////////////
#endif
////////////////////////////////////////////////////////////////////////////////
